//导出一个类
let _Vue = null;
export default class VueRouter {
  //定义一个install静态方法
  //传入Vue的构造函数
  //vue.use调用install方法时传入两个参数，第一个是vue的构造函数,第二个是可选的选项对象
  static install(Vue) {
    //1.判断当前插件是否被安装
    //installed是自己增加的属性-true表示已安装
    if (VueRouter.install.installed) {
      return;
    }
    VueRouter.install.installed = true;
    //2.把Vue构造函数记录到全局变量(因为在vuerouter的实例方法中使用这个Vue的构造函数)
    _Vue = Vue;
    //3.把创建Vue实例时候传入的router对象注入到Vue实例上
    //使用混入
    _Vue.mixin({
      beforeCreate() {
        //防止多次判断
        //vue的实例属性$options是用来获取定义在data外的数据和方法的。
        //这个router是创建vue实例传入的在main.js文件里面可以就看到;
        if (this.$options.router) {
          //组件不执行，vue实例执行，组件没有router属性
          //  this指向vue实例
          _Vue.prototype.$router = this.$options.router;
          //调用init方法
          //这就是VueRouter类
          this.$options.router.init();
        }
      },
    });
  }

  //构造函数,里面初始化3个属性
  constructor(options) {
    //初始化3个值
    //作用是记录这个对象传入的options
    this.options = options;
    //这个对象的是将来把options中传入的router(规则)解析出来存储到routerMap对象里面
    this.routerMap = {};
    //这是一个响应式的对象（使用vue中提供的observable,他会转换为响应式的对象）
    this.data = _Vue.observable({
      //作用是存储当前的路由地址,现在的value是默认情况
      current: "/",
    });
  }

  //作用是将下面两个方法包装起来
  init() {
    this.createRouterMap();
    this.initComponents(_Vue);
    this.initEvent();
  }

  //作用是把构造函数传过来的选项中的router,转换成键值对的形式存储到构造函数中的routerMap中
  //遍历所有的路由规则,把路由规则解析成键值对的形式存储到routerMap对象中
  createRouterMap() {
    //routes(这是存放路由地址的数组)
    this.options.routes.forEach((route) => {
      this.routerMap[route.path] = route.component;
    });
  }

  //router-link
  //接收一个Vue的构造函数作为参数
  initComponents(Vue) {
    //使用vue创建组件
    //名字，选项
    Vue.component("router-link", {
      //接受外部传来参数
      props: {
        to: String,
      },
      //运行时版本不支持template,所以先注释起来
      //因为没有编译器,所以没办法将模板转为render函数
      // template:'<a :href="to"><slot></slot></a>'
      // ===================
      // h函数的作用是帮我们创建虚拟DOM
      render(h) {
        //这个h函数是vue传过来的
        //h函数可以接收3个参数
        //第一个参数是我们要创建的这个元素对应的选择器(例如标签选择器)
        //第二个参数可以给这个标签设置一些属性
        //第三个参数可以设置我们生成元素的子元素
        return h(
          "a",
          {
            //通过attrs来设置属性
            attrs: {
              href: this.to,
            },
            //router-view
            on: {
              //注册点击事件
              click: this.clickGandler,
            },
            //这是a标签里面的内容，可以是多个所以是数组
            //这种写法是获取默认插槽的内容
          },
          [this.$slots.default]
        );
      },
      //router-view阻止点击事件
      methods: {
        clickGandler(e) {
          //第一个参数data,第二个参数是title,第三个参数是url
          history.pushState({}, "", this.to);
          //加载对应组件(让下面重新渲染视图)
          this.$router.data.current = this.to;
          e.preventDefault();
        },
      },
    });

    const self = this;
    Vue.component("router-view", {
      render(h) {
        //当前路由组件
        const component = self.routerMap[self.data.current];
        //使用h函数把组件转化为虚拟DOM
        return h(component);
      },
    });
  }

  //浏览器前进后退功能
  initEvent() {
    window.addEventListener("popstate", () => {
      this.data.current = window.location.pathname;
    });
  }
}
